package util;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;


public class UniprotXML {

	public final String UNIPROTSERVER = "http://www.uniprot.org/uniprot/";
	/**
	 * 
	 * @param proteinName the name of the protein that we want to extract locations for
	 * @return a set of PubMed IDs
	 * @throws ParserConfigurationException
	 * @throws SAXException
	 * @throws IOException
	 * @throws XPathExpressionException
	 */
	public Set<String> getPubMedIdsForLocations(String proteinName) throws ParserConfigurationException, SAXException, IOException, XPathExpressionException {
		FileInputStream file = new FileInputStream(new File("xmls/"+proteinName+".xml"));
		DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
		DocumentBuilder builder =  builderFactory.newDocumentBuilder();
		Document xmlDocument = builder.parse(file);
		XPath xPath =  XPathFactory.newInstance().newXPath();
		
		NodeList list = (NodeList) xPath.compile("//comment[@type='subcellular location']/text[@evidence]").evaluate(xmlDocument,XPathConstants.NODESET);
		return extractEvidenceKeys(list, xPath, xmlDocument);
	}
	/**
	 * 
	 * @param proteinName the name of the protein that we want to extract mutations for
	 * @return a set of PubMed IDs
	 * @throws ParserConfigurationException
	 * @throws SAXException
	 * @throws IOException
	 * @throws XPathExpressionException
	 */
	public Set<String> getPubMedIdsForMutations(String proteinName) throws ParserConfigurationException, SAXException, IOException, XPathExpressionException {
		//create the needed objects for XPath query evaluation
		FileInputStream file = new FileInputStream(new File("xmls/"+proteinName+".xml"));
		DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
		DocumentBuilder builder =  builderFactory.newDocumentBuilder();
		Document xmlDocument = builder.parse(file);
		XPath xPath =  XPathFactory.newInstance().newXPath();

		//find all the features that have a variations and have evidence 
		NodeList list = (NodeList) xPath.compile("//feature[@type='sequence variant'][@evidence]").evaluate(xmlDocument,XPathConstants.NODESET);
		return extractEvidenceKeys(list, xPath, xmlDocument);
	}
	
	private Set<String> extractEvidenceKeys(NodeList list, XPath xPath, Document xmlDocument) throws XPathExpressionException
	{
		//iterate over all the evidence keys and save the only once
		Set<Integer> evidenceKeys = new HashSet<Integer>();
		for(int i=0; i<list.getLength(); i++)
		{
			String evidence = list.item(i).getAttributes().getNamedItem("evidence").getNodeValue();
			String evidenceKey[] = evidence.split(" ");
			for(int j=0; j<evidenceKey.length; j++)
			{
				evidenceKeys.add(Integer.parseInt(evidenceKey[j]));
			}
		}
		Set<String> pubMedIDs = new HashSet<String>();

		//iterate over all the evidence keys and get the pub med id
		Iterator<Integer> it = evidenceKeys.iterator();
		while(it.hasNext()){
			String id = xPath.compile("//evidence[@key='"+it.next()+"']/source/dbReference[@type='PubMed']/@id").evaluate(xmlDocument);
			pubMedIDs.add(id);
		}

		return pubMedIDs;
	}
	/**
	 * 
	 * @param fileName - the name of the file that we want to download from the Uniprot server that has the same name as the protein
	 * @throws IOException
	 */
	public void downloadXMLfromUniprot(String fileName) throws IOException{
		URL website = new URL(UNIPROTSERVER+fileName+".xml");
		ReadableByteChannel rbc = Channels.newChannel(website.openStream());
		if(!FileUtil.pathExists("xmls/")) {
			FileUtil.createDirectory("xmls/");
		}
		FileOutputStream fos = new FileOutputStream("xmls/"+fileName+".xml");
		fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
		fos.close();
	}
}
